package com.xbongbong.pro.service.toolbox.statistic.help;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.UnitEnum;
import com.xbongbong.paas.help.CommonHelp;
import com.xbongbong.sys.help.PackageHelp;
import com.xbongbong.paas.pojo.IdNamePojo;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.redis.RedisTimeConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.JsonHelperUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.CommonConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.domain.entity.ChartEntity;
import com.xbongbong.pro.enums.PackageTypeEnum;
import com.xbongbong.pro.enums.TimeTypeEnum;
import com.xbongbong.pro.enums.errorcodes.PerformanceErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.StatisticsServicesErrorCodeEnum;
import com.xbongbong.pro.performance.pojo.PerformancePojo;
import com.xbongbong.pro.performanceset.pojo.PerformanceChartLogOrderPojo;
import com.xbongbong.pro.performanceset.pojo.PerformanceSetExplainPojo;
import com.xbongbong.pro.statistic.enums.ChartTypeEnum;
import com.xbongbong.pro.statistic.enums.StatisticsTypeEnum;
import com.xbongbong.pro.statistic.model.ChartCustomModel;
import com.xbongbong.pro.statistic.model.ChartModel;
import com.xbongbong.pro.statistic.pojo.FiscalYearTimePojo;
import com.xbongbong.pro.statistic.result.pojo.ChartResultPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.PerformanceEntity;
import com.xbongbong.saas.enums.AssessTypeEnum;
import com.xbongbong.saas.enums.MinCycleEnum;
import com.xbongbong.saas.model.PerformanceModel;
import com.xbongbong.sys.domain.entity.DepartmentEntity;
import com.xbongbong.sys.domain.entity.UserEntity;
import com.xbongbong.sys.enums.CompanyStructTypeEnum;
import com.xbongbong.sys.enums.DataPermissionEnum;
import com.xbongbong.sys.model.CompanyConfigModel;
import com.xbongbong.sys.model.DepartmentModel;
import com.xbongbong.sys.model.UserModel;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * @description: 业绩目标帮助类
 * 1. 固定数值，动态数值计算
 * @Author: hongxiao
 * @date: 2019-02-21 09:43
 * @since v1.0
 */
@Component
public class PerformanceHelp {
    @Resource
    private DepartmentModel departmentModel;
    @Resource
    private UserModel userModel;
    @Resource
    private ChartModel chartModel;
    @Resource
    private ChartCustomModel chartCustomModel;
    @Resource
    private CompanyConfigModel companyConfigModel;
    @Resource
    private PerformanceModel performanceModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private ChartDtoHelp chartDtoHelp;
    @Resource
    private CommonHelp commonHelp;
    @Resource
    private PackageHelp packageHelp;

    /**
     * 获取考核对象列表---业绩目标只考核部门
     * @param setEntity 指标实体
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-26 11:29
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public List<IdNamePojo> getPermissionList(ChartEntity setEntity) {
        String permissionValue = "";
        if(setEntity.getPermissions() != null) {
            JSONObject visibleObj = setEntity.getPermissions().getJSONObject("visibleScopeRule");
            if (visibleObj != null) {
                permissionValue = visibleObj.getString("dep");
            } else {
                // 兼容老数据
                permissionValue = setEntity.getPermissions().getString("dep");
            }
        }
        List<IdNamePojo> permissionList = JsonHelperUtil.parseArray(permissionValue, IdNamePojo.class);
        if(permissionList.size() == 0) {
            IdNamePojo pojo = new IdNamePojo();
            pojo.setId(PaasConstant.CORP_DEPARTMENT_ID);
            pojo.setName(I18nMessageUtil.getMessage(CommonConstant.COMPANY_WIDE));
            permissionList.add(pojo);
        }
        return permissionList;
    }
    /**
     * 获取登录员工能看到的指标list（根据考核对象-超管看到所有的）
     * @param userEntity 员工
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-19 19:36
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public List<ChartEntity> getViewList(UserEntity userEntity) {
        String corpid = userEntity.getCorpid();
        // 获取自己公司启用的指标列表
        List<ChartEntity> list = chartModel.getList(corpid, BasicConstant.IS_USE, ChartTypeEnum.PERFORMANCE.getCode());
        return getViewList(userEntity, list);
    }

    /**
     * 获取登录员工能看到的指标list（根据考核对象-超管看到所有的）
     * @param userEntity 员工
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-19 19:36
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public List<ChartEntity> getViewList(UserEntity userEntity,String orderByStr) {
        String corpid = userEntity.getCorpid();
        // 获取自己公司启用的指标列表
        List<ChartEntity> list = chartModel.getList(corpid, null, ChartTypeEnum.PERFORMANCE.getCode(),orderByStr);
        return getViewList(userEntity, list);
    }
    
    /**
     * 获取登录员工能看到的指标list，自定义目标
     * @param userEntity
     * @param chartResultPojo
     * @return java.util.List<com.xbongbong.pro.domain.entity.ChartEntity>
     * @throws XbbException
     * @author xinpeng.jiang
     * @date 2022/3/15 19:07
     * @version 1.0
     */
    public List<ChartEntity> getViewList4Customer(UserEntity userEntity, ChartResultPojo chartResultPojo) throws XbbException {
        chartDtoHelp.dealWithIdIn(chartResultPojo);
        // 获取chart
        Map<String, Object> params = new HashMap<>(3);
        params.put(ParameterConstant.CORPID, userEntity.getCorpid());
        params.put(ParameterConstant.ID_IN, chartResultPojo.getCustomIdIn());
        params.put(ParameterConstant.DEL, DelEnum.NORMAL.getDel());
        List<ChartEntity> list = chartCustomModel.findEntitys(params);
        return getViewList(userEntity, list);
    }
    
    /**
     * 获取登录员工能看到的指标list（根据考核对象-超管看到所有的）
     * @param indexFlag true代表千人千面请求，不在考核范围的不能过滤，而是要返回一个不在考核范围的信息（因此，首页时要先获取到所有的，防止一些不在考核范围内的，结果首页有该指标展示，而获取不到指标-不在范围首页有的，一句话提示即可）
     * @param userEntity 登录员工
     * @param dataPermission 登录员工的数据权限
     * @param list 图表list
     * @return java.util.List<com.xbongbong.pro.domain.entity.ChartEntity>
     * @author zcp
     * @date 2019/3/27 21:21
     * @since v1.0
     * @version v1.0
     */
    public List<ChartEntity> getViewList(boolean indexFlag, UserEntity userEntity, Integer dataPermission, List<ChartEntity> list) {
        String corpid = userEntity.getCorpid();
        if (indexFlag || DataPermissionEnum.ALL.getCode().equals(dataPermission)) {
            // 超管角色查看所有
            return list;
        }
        String userId = userEntity.getUserId();
        /*
         * 最后判断考核对象是否包含员工
         */
        List<ChartEntity> viewList = new ArrayList<>();
        for (ChartEntity entity : list) {
            List<IdNamePojo> permissionList = getPermissionList(entity);
            boolean flag = checkViewPerformanceSetByUserId(corpid, userId, permissionList, entity.getId());
            if (flag) {
                viewList.add(entity);
            }
        }
        return viewList;
    }

    /**
     * 判断是否有查看权限
     * @param corpid         公司id
     * @param userId         操作人id
     * @param permissionList 考核对象
     * @param id             业绩目标指标id
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-19 19:45
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public boolean checkViewPerformanceSetByUserId(String corpid, String userId, List<IdNamePojo> permissionList, Long id) {
        if (permissionList == null || permissionList.size() == 0) {
            return false;
        }
        for (IdNamePojo pojo : permissionList) {
            Long depId = pojo.getId();
            if (depId != null && depId.equals(1L)) {
                // 全公司都可以查看
                return true;
            }
        }
        // 考核的员工id列表
        Set<String> userIdIn = getPermission2UserIdInFromJedis(corpid, permissionList, id);
        // 判断是否包含该员工
        return userIdIn.contains(userId);
    }
    /**
     * 判断是否有查看权限
     * @param corpid 公司id
     * @param departmentId 部门id
     * @param permissionList 考核对象
     * @param id 业绩目标指标id
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-19 19:54
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public boolean checkViewPerformanceSetByDeptId(String corpid, Long departmentId, List<IdNamePojo> permissionList, Long id) {
        if (permissionList == null || permissionList.size() == 0) {
            return false;
        }
        for (IdNamePojo pojo : permissionList) {
            Long depId = pojo.getId();
            boolean flag = depId != null && depId.equals(1L) || depId.equals(departmentId);
            if (flag) {
                // 全公司都可以查看
                return true;
            }
        }
        // 考核的员工id列表
        List<Long> depIdIn = getPermission2DepIdInFromJedis(corpid, id, permissionList);
        // 判断是否包含该员工
        return depIdIn.contains(departmentId);
    }
    /**
     * 从缓存获取 考核的员工ids
     * @param corpid 公司id
     * @param list   考核对象
     * @param setId  指标id
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-20 16:21
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public Set<String> getPermission2UserIdInFromJedis(String corpid, List<IdNamePojo> list, Long setId) {
        // 缓存
        String sessionName4User = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_USER_ID, setId);
        String redisUserIdIn = paasRedisHelper.getValue(sessionName4User, corpid);
        List<String> userIdIn = JsonHelperUtil.parseArray(redisUserIdIn, String.class);
        if (userIdIn != null && userIdIn.size() > 0) {
            return new HashSet<>(userIdIn);
        }
        Set<String> userIdSet = new HashSet<>();
        /*
         * 如果缓存没有该指标的考核员工ids，则要算出并置入缓存，按照以下步骤：
         * （1）如果缓存内没有该指标的考核员工ids，则再读取缓存内指标的考核部门ids；再通过考核部门ids算出考核员工ids
         * （2）如果缓存也没有考核部门ids，则直接读库算出考核员工ids
         */
        String sessionName4Dep = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_DEP_ID, setId);
        String redisDepIdIn = paasRedisHelper.getValue(sessionName4Dep, corpid);

        List<Long> depIdList = JsonHelperUtil.parseArray(redisDepIdIn, Long.class);
        if (depIdList == null || depIdList.size() == 0) {
            // 缓存内也无部门，则查库（该方法内会把查出的结果置入缓存）
            userIdSet = getPermission2UserIdIn(corpid, setId, list, null);
        } else {
            // 包含离职员工
            userIdSet.addAll(userModel.getDepUserIdList4Visible(corpid, depIdList, 1));
            userIdSet.add("-1");

            // 置入缓存
            paasRedisHelper.setValue(sessionName4User, corpid, userIdSet, RedisTimeConstant.HOUR_DURATION);
        }

        return userIdSet;
    }
    /**
     * 根据指标的考核对象转换为员工userIds（包含下级部门的员工ids）
     * 注：此方法未排除非下属员工，只是把指标设置的考核对象转化为所有员工id列表
     *
     * @param corpid     公司id
     * @param setId      指标id
     * @param list       考核对象列表
     * @param nameDepMap 部门名称map
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-01-31 16:33
     * @version v1.0
     * @since v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public Set<String> getPermission2UserIdIn(String corpid, Long setId, List<IdNamePojo> list, Map<Long, String> nameDepMap) {
        Set<Long> depIdIn = new HashSet<>();
        if (list == null || list.size() == 0) {
            return new HashSet<>();
        }
        String rootDepName = null;
        Long rootDepId = null;
        if (list.size() == 1) {
            rootDepId = list.get(0).getId();
            rootDepName = list.get(0).getName();
        }
        for (IdNamePojo pojo : list) {
            depIdIn.add(pojo.getId());
        }
        // 获取部门所有的下属部门的 部门ids
        List<DepartmentEntity> depList = departmentModel.getSubDepListFromJedis(corpid, depIdIn);
        if (nameDepMap == null) {
            for (DepartmentEntity departmentEntity : depList) {
                depIdIn.add(departmentEntity.getId());
            }
        } else {
            for (DepartmentEntity departmentEntity : depList) {
                Long depId = departmentEntity.getId();
                depIdIn.add(depId);
                nameDepMap.put(depId, departmentEntity.getName());
            }
        }
        Set<String> userIdIn = new HashSet<>();
        if (Objects.equals(PaasConstant.CORP_DEPARTMENT_ID, rootDepId) && Objects.equals(I18nMessageUtil.getMessage(CommonConstant.COMPANY_WIDE), rootDepName)) {
            // 全公司员工(不包含不在可见范围内的员工)
            List<String> userIds = userModel.getSubIdListByDataPermission4Visible(DataPermissionEnum.ALL.getCode(), PaasConstant.SYS_USER_ID, corpid, 1, true);
            userIdIn.addAll(userIds);
        } else {
            // 包含离职员工
            userIdIn.addAll(userModel.getDepUserIdList4Visible(corpid, depIdIn, 1));
            userIdIn.add("-1");
        }
        // 把查询出的考核对象的部门ids，考核员工ids都置入缓存
        String sessionName4User = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_USER_ID, setId);
        String sessionName4Dep = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_DEP_ID, setId);
        // 置入缓存
        paasRedisHelper.setValue(sessionName4User, corpid, userIdIn, RedisTimeConstant.HOUR_DURATION);
        paasRedisHelper.setValue(sessionName4Dep, corpid, depIdIn, RedisTimeConstant.HOUR_DURATION);
        return userIdIn;
    }
    /**
     * 从缓存获取 考核的部门ids
     * @param corpid          公司id
     * @param setId           指标id
     * @param permissionArray 考核对象
     * @return java.util.List<java.lang.Long> 
     * @author zcp
     * @date 2019/3/27 15:33
     * @since v1.0
     * @version v1.0
     */
    public List<Long> getPermission2DepIdInFromJedis(String corpid, Long setId, List<IdNamePojo> permissionArray){
        // 缓存
        String sessionName4User = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_DEP_ID, setId);
        String redisUserIdIn = paasRedisHelper.getValue(sessionName4User, corpid);
        List<Long> depIdList = JsonHelperUtil.parseArray(redisUserIdIn, Long.class);
        if (depIdList != null && depIdList.size() > 0) {
            return depIdList;
        }
        //该方法内已经置入缓存内
        return getPermission2DepIdIn(corpid, setId, permissionArray);
    }
    /**
     * 获取考核对象对应的部门id列表
     *
     * @param corpid          公司id
     * @param setId           指标id
     * @param permissionArray 考核对象
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-20 20:56
     * @since v1.0
     * @version v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public List<Long> getPermission2DepIdIn(String corpid, Long setId, List<IdNamePojo> permissionArray) {
        // 根据指标的考核对象转换为部门ids（包含子级部门的ids）
        Set<Long> depIdIn = new HashSet<>();
        for (IdNamePojo depObject : permissionArray) {
            depIdIn.add(depObject.getId());
        }

        // 获取部门所有的下属部门的 部门ids
        if (depIdIn.size() > 0) {
            List<DepartmentEntity> depList = departmentModel.getSubDepListFromJedis(corpid, depIdIn);
            for (DepartmentEntity departmentEntity : depList) {
                depIdIn.add(departmentEntity.getId());
            }
        }

        // 获取考核对象的所有下属部门ids
        depIdIn.addAll(depIdIn);
        depIdIn.add(-1L);
        // 置入缓存
        String sessionName4Dep = createJedisName4Set(RedisPrefixConstant.PERFORMANCE_PERMISSION_DEP_ID, setId);
        paasRedisHelper.setValue(sessionName4Dep, corpid, depIdIn, RedisTimeConstant.HOUR_DURATION);
        return new ArrayList<>(depIdIn);
    }

    /**
     * 缓存名称
     *
     * @param prefix 前缀
     * @param setId  指标id
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-01-31 16:31
     * @version v1.0
     * @since v1.0
     * @update by zcp 从main-service移入 2019-03-27
     */
    public String createJedisName4Set(String prefix, Long setId) {
        return prefix + "_" + setId;
    }

    /**
     * 获取业绩指标扩展字段pojo，包含：minCycle、assessType、fixedValue、accessProductIds
     * @param chartEntity 指标实体
     * @return com.xbongbong.paas.pojo.PerformanceSetExplainPojo
     * @author zcp
     * @date 2019/3/28 19:10
     * @since v1.0
     * @version v1.0
     */
    public PerformanceSetExplainPojo getPerformanceSetExplainPojo(ChartEntity chartEntity) {
        PerformanceSetExplainPojo explainPojo = new PerformanceSetExplainPojo();
        if (chartEntity == null) {
            return explainPojo;
        }
        if (chartEntity.getExplains() != null) {
            try {
                explainPojo = JSON.toJavaObject(chartEntity.getExplains(), PerformanceSetExplainPojo.class);
            } catch (JSONException e) {
                explainPojo = new PerformanceSetExplainPojo();
            }
        }
        return explainPojo;
    }

    /**
     * 计算固定数值型目标额（根据筛选时间的不同，要对固定值进行乘除运算）
     *
     * @param setEntity 指标实体
     * @param timeTypeEnum 筛选时间类型
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @param startTime 筛选开始时间--主要是自定义筛选用
     * @param endTime 筛选结束时间--主要是自定义筛选用
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 09:58
     * @since v1.0
     * @version v1.0
     */
    public static Double getFixedData(ChartEntity setEntity, TimeTypeEnum timeTypeEnum, Double workingDayWeek, Double workingDayMonth, Integer startTime, Integer endTime) {
        double target = 0;
        PerformanceSetExplainPojo explainPojo = new PerformanceSetExplainPojo();
        if (setEntity.getExplains() != null) {
            explainPojo = JSON.toJavaObject(setEntity.getExplains(), PerformanceSetExplainPojo.class);
        }
        Double fixedValue = explainPojo.getFixedValue();
        //最小考核周期
        MinCycleEnum minCycleEnum = MinCycleEnum.getByCode(explainPojo.getMinCycle());
        switch (minCycleEnum) {
            case MONTH:
                target = calcFixedValue4Month(fixedValue, timeTypeEnum, workingDayWeek, workingDayMonth, startTime, endTime);
                break;
            case WEEK:
                target = calcFixedValue4Week(fixedValue, timeTypeEnum, workingDayWeek, workingDayMonth, startTime, endTime);
                break;
            case DAY:
                target = calcFixedValue4Day(fixedValue, timeTypeEnum, workingDayWeek, workingDayMonth, startTime, endTime);
                break;

            default:
                break;
        }
        return target;
    }

    /**
     * 获取部门的固定目标值，规则为：部门员工数*每人的固定值
     *
     * @param corpid
     * @param fixedTarget 每人的固定值
     * @param companyStructTypeEnum 组织架构类型：1员工的业绩 2部门的业绩
     * @param uidInSet 部门下的员工ids
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 10:08
     * @since v1.0
     * @version v1.0
     */
    public Double getFixedData4Dep(String corpid, Double fixedTarget, CompanyStructTypeEnum companyStructTypeEnum, Set<String> uidInSet) {
        if (fixedTarget == null || fixedTarget == -1 || companyStructTypeEnum == CompanyStructTypeEnum.USER) {
            return fixedTarget;
        }
        if (uidInSet == null || uidInSet.size() == 0) {
            return 0D;
        }
        uidInSet.remove("");
        uidInSet.remove("0");
        uidInSet.remove("-1");
        fixedTarget *= uidInSet.size();
        return fixedTarget;
    }


    /**
     * 计算动态数值型目标额（根据筛选时间的不同，要对固定值进行乘除运算）
     *
     * @param targetObject 目标
     * @param setEntity 指标实体
     * @param timeTypeEnum 筛选时间类型
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @param fiscalYearTimePojo 筛选时间参数pojo
     * @param fiscalStartTime 财年起始时间戳
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-21 09:58
     * @since v1.0
     * @version v1.0
     */
    public static Double getChangeData(JSONObject targetObject, ChartEntity setEntity, TimeTypeEnum timeTypeEnum,
                                       FiscalYearTimePojo fiscalYearTimePojo, Double workingDayWeek, Double workingDayMonth,
                                       Integer fiscalStartTime) throws XbbException {
        Double target = -1D;
        if (targetObject.size() == 0) {
            return target;
        }
        PerformanceSetExplainPojo explainPojo = new PerformanceSetExplainPojo();
        if (setEntity.getExplains() != null) {
            explainPojo = JSON.toJavaObject(setEntity.getExplains(), PerformanceSetExplainPojo.class);
        }
        //最小考核周期
        MinCycleEnum minCycleEnum = MinCycleEnum.getByCode(explainPojo.getMinCycle());
        switch (minCycleEnum) {
            case MONTH:
                target = calcChangeValue4Month(targetObject, fiscalYearTimePojo, timeTypeEnum, workingDayWeek,
                        workingDayMonth);
                break;
            case WEEK:
                target = calcChangeValue4Week(targetObject, fiscalYearTimePojo, timeTypeEnum, workingDayWeek,
                        fiscalStartTime);
                break;
            default:
                break;
        }
        target = (target == null)? Double.valueOf(-1): target;
        return target;
    }
    /**
     * 获取组织架构类型枚举（员工id或者部门id必传其一）
     * @param companyStructType 组织架构类型：1员工、获取员工业绩，2部门、获取部门业绩
     * @param checkedUserId 筛选的员工id
     * @param checkedDepId 筛选的部门id
     * @return com.xbongbong.sys.enums.CompanyStructTypeEnum 
     * @throws XbbException
     * @author zcp
     * @date 2019/4/1 10:28
     * @since v1.0
     * @version v1.0
     */
    public CompanyStructTypeEnum getCompanyStructTypeEnum4Must(Integer companyStructType, String checkedUserId, Long checkedDepId) throws XbbException {
        CompanyStructTypeEnum companyStructTypeEnum = getCompanyStructTypeEnum(companyStructType);
        if (CompanyStructTypeEnum.USER == companyStructTypeEnum && StringUtil.isEmpty(checkedUserId)) {
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224022, StatisticsServicesErrorCodeEnum.API_ERROR_224022.getMsg(), I18nMessageUtil.getMessage(StringConstant.SELECT_SUB_USER_TAG));
        } else if (CompanyStructTypeEnum.DEPARTMENT == companyStructTypeEnum && checkedDepId == null) {
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224022, StatisticsServicesErrorCodeEnum.API_ERROR_224022.getMsg(), I18nMessageUtil.getMessage(I18nStringConstant.SELECT_DEPARTMENT));
        }
        return companyStructTypeEnum;
    }
    /**
     * 获取组织架构类型枚举（只检查是否传companyStructType，不检查是否筛选id）
     * @param companyStructType 组织架构类型：1员工、获取员工业绩，2部门、获取部门业绩
     * @return com.xbongbong.sys.enums.CompanyStructTypeEnum 
     * @throws XbbException
     * @author zcp
     * @date 2019/4/4 10:40
     * @since v1.0
     * @version v1.0
     */
    public CompanyStructTypeEnum getCompanyStructTypeEnum(Integer companyStructType) throws XbbException {
        CompanyStructTypeEnum companyStructTypeEnum = CompanyStructTypeEnum.getByCode(companyStructType);
        if (companyStructTypeEnum == CompanyStructTypeEnum.UNKNOWN) {
            throw new XbbException(StatisticsServicesErrorCodeEnum.API_ERROR_224020, StatisticsServicesErrorCodeEnum.API_ERROR_224020.getMsg(), I18nMessageUtil.getMessage(I18nStringConstant.PERFORMANCE_TYPE));
        }
        return companyStructTypeEnum;
    }
    /**
     * 获取业绩目标值
     * @param companyStructTypeEnum 公司架构类型枚举, 筛选组织架构使用 1员工，2部门
     * @param chartResultPojo 统计参数pojo
     * @param chartEntity 图表
     * @return java.lang.Double
     * @throws XbbException
     * @author zcp
     * @date 2019/3/28 19:34
     * @since v1.0
     * @version v1.0
     */
    public Double performanceTarget(CompanyStructTypeEnum companyStructTypeEnum, ChartResultPojo chartResultPojo, ChartEntity chartEntity) throws XbbException {
        String corpid = chartResultPojo.getCorpid();
        FiscalYearTimePojo fiscalYearTimePojo = new FiscalYearTimePojo();
        //dealWithFiscalYear方法内部有对fiscalYearTimePojo值的处理更改
        BeanUtil.copyProperties(chartResultPojo.getFiscalYearTimePojo(), fiscalYearTimePojo);
        //财年开始月份
        int startMonth = fiscalYearTimePojo.getStartMonth();
        Integer timeType = fiscalYearTimePojo.getTimeType4DefaultMonth();
        TimeTypeEnum timeTypeEnum = TimeTypeEnum.getByCode(timeType);
        Integer startTime = chartResultPojo.getStartTime();
        Integer endTime = chartResultPojo.getEndTime();
        Double workingDayWeek = companyConfigModel.getWorkingDayWeek(corpid);
        Double workingDayMonth = companyConfigModel.getWorkingDayMonth(corpid);

        //业绩类型：1员工的业绩 2部门的业绩
        CompanyStructTypeEnum objectiveTypeEnum = (CompanyStructTypeEnum.USER == companyStructTypeEnum) ? CompanyStructTypeEnum.USER : CompanyStructTypeEnum.DEPARTMENT;
        // 考核方式：1动态数值型,2固定数值型
        PerformanceSetExplainPojo explainPojo = getPerformanceSetExplainPojo(chartEntity);
        Integer assessType = explainPojo.getAssessType();
        Double target = -1D;
        if (assessType == AssessTypeEnum.FIXED.getCode()) {
            //------------------固定数值型
            //处理固定目标
            target = getFixedData(chartEntity, timeTypeEnum, workingDayWeek, workingDayMonth, startTime, endTime);
            Set<String> uidInSet = new HashSet<>(chartResultPojo.getUserIdIn());
            //处理固定目标（如果是部门目标，则乘以员工数量）
            target = getFixedData4Dep(corpid, target, objectiveTypeEnum, uidInSet);
        }else {
            //------------------动态数值型
            //处理财年年份：处理一些当前时间跨财年的情况
            Integer fiscalYear = FiscalYearHelp.dealWithFiscalYear(timeTypeEnum, fiscalYearTimePojo, startTime, endTime);
            //财年起始时间戳
            Integer fiscalStartTime = FiscalYearHelp.getFiscalYearStartInt(fiscalYear, startMonth);
            PerformancePojo performancePojo = new PerformancePojo();
            performancePojo.setYear(fiscalYear);
            performancePojo.setSetId(chartEntity.getId());
            performancePojo.setCompanyStructType(objectiveTypeEnum.getCode());
            performancePojo.setUserId(chartResultPojo.getCheckedUserId());
            performancePojo.setDepartmentId(chartResultPojo.getCheckedDepId());
            performancePojo.setMonth(fiscalYearTimePojo.getMonth());
            performancePojo.setStatisticsType(chartEntity.getStatisticsType());
            PerformanceEntity performanceEntity = performanceModel.getPerformanceEntity(corpid, performancePojo);
            JSONObject targetObject = (performanceEntity == null) ? null : performanceEntity.getTarget();
            if (targetObject != null && targetObject.size() != 0) {
                //获取业绩目标
                target = getChangeData(targetObject, chartEntity, timeTypeEnum,
                        fiscalYearTimePojo, workingDayWeek, workingDayMonth, fiscalStartTime);
            }
        }
        return target;
    }

    //----------------------------------------private方法----------------------------------------
    /**
     * 固定数值型目标处理--最小考核周期为“月”
     *
     * @param fixedValue 固定数值
     * @param timeTypeEnum 时间枚举
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @param startTime 筛选开始时间--主要是自定义筛选用
     * @param endTime 筛选结束时间--主要是自定义筛选用
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 09:54
     * @since v1.0
     * @version v1.0
     */
    private static Double calcFixedValue4Month(Double fixedValue, TimeTypeEnum timeTypeEnum, Double workingDayWeek, Double workingDayMonth, Integer startTime, Integer endTime) {
        double value = 0D;
        switch (timeTypeEnum) {
            //全年、上半年 开始时间一样
            case YEAR:
                value = 12 * fixedValue;
                break;
            //上半年
            case YEAR_HALF_UP:
            //下半年
            case YEAR_HALF_DOWN:
                value = 6 * fixedValue;
                break;
            //季度
            case SEASON:
                value = 3 * fixedValue;
                break;
            //本月，某一月---//某年某月
            case MONTH:
            //上月
            case MONTH_LAST:
                value = fixedValue;
                break;
            //周
            case WEEK:
            //上周
            case WEEK_LAST:
                //周目标=每月固定频率÷每月天数×每周天数
                value = Arith.div(fixedValue, workingDayMonth) * workingDayWeek;
                break;
            //本日or某一日
            case DAY:
            //昨日
            case DAY_LAST:
                //天目标 = 每月固定频率÷每月天数
                value = Arith.div(fixedValue, workingDayMonth);
                break;
            //自定义---自己选择开始结束时间
            case CUSTOM:
                Integer count = (endTime - startTime) / 86400;
                count = (count == 0)? Integer.valueOf(1): count;
                //自定义时间目标 = 每月固定频率÷每月天数 * （总天数）
                value = Arith.div(fixedValue, workingDayMonth) * count;
                break;
            default:
                break;
        }
        return value;
    }


    /**
     * 固定数值型目标处理--最小考核周期为“周”
     *
     * @param fixedValue 固定数值
     * @param timeTypeEnum 时间枚举
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @param startTime 筛选开始时间--主要是自定义筛选用
     * @param endTime 筛选结束时间--主要是自定义筛选用
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 09:56
     * @since v1.0
     * @version v1.0
     */
    private static Double calcFixedValue4Week(Double fixedValue, TimeTypeEnum timeTypeEnum, Double workingDayWeek, Double workingDayMonth, Integer startTime, Integer endTime) {
        double value = 0D;
        switch (timeTypeEnum) {
            //全年、上半年 开始时间一样
            case YEAR:
                //年目标=12*月目标 
                //月目标 = 每周固定频率÷每周天数×每月天数
                value = Arith.div(fixedValue, workingDayWeek) * workingDayMonth;
                value = 12 * value;
                break;
            //上半年
            case YEAR_HALF_UP:
            //下半年
            case YEAR_HALF_DOWN:
                value = Arith.div(fixedValue, workingDayWeek) * workingDayMonth;
                value = 6 * value;
                break;
            //季度
            case SEASON:
                value = Arith.div(fixedValue, workingDayWeek) * workingDayMonth;
                value = 3 * value;
                break;
            //本月，某一月---//某年某月
            case MONTH:
            //上月
            case MONTH_LAST:
                //月目标 = 每周固定频率÷每周天数×每月天数
                value = Arith.div(fixedValue, workingDayWeek) * workingDayMonth;
                break;
            //周
            case WEEK:
            //上周
            case WEEK_LAST:
                value = fixedValue;
                break;
            //本日or某一日
            case DAY:
            //昨日
            case DAY_LAST:
                //天目标 = 每周固定频率÷每周天数
                value = Arith.div(fixedValue, workingDayWeek);
                break;
            //自定义---自己选择开始结束时间
            case CUSTOM:
                Integer count = (endTime - startTime) / TimeConstant.SECONDS_PER_DAY;
                count = (count == 0)? Integer.valueOf(1): count;
                //自定义时间目标 = 每周固定频率÷每周天数 * （总天数）
                value = Arith.div(fixedValue, workingDayWeek) * count;
                break;
            default:
                break;
        }
        return value;
    }

    
    /**
     * 固定数值型目标处理--最小考核周期为“天”
     *
     * @param fixedValue 固定数值
     * @param timeTypeEnum 时间枚举
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @param startTime 筛选开始时间--主要是自定义筛选用
     * @param endTime 筛选结束时间--主要是自定义筛选用
     * @return
     * @throws
     *
     * @author hongxiao
     * @date: 2019-02-21 09:51
     * @since v1.0
     * @version v1.0
     */
    private static Double calcFixedValue4Day(Double fixedValue, TimeTypeEnum timeTypeEnum, Double workingDayWeek, Double workingDayMonth, Integer startTime, Integer endTime) {
        double value = 0D;
        switch (timeTypeEnum) {
            //全年、上半年 开始时间一样
            case YEAR:
                //年目标=12*月目标
                //月目标 = 每月天数*每天固定频率
                value = BasicConstant.MAX_MONTH * fixedValue * workingDayMonth;
                break;
            //上半年
            case YEAR_HALF_UP:
            //下半年
            case YEAR_HALF_DOWN:
                value = 6 * fixedValue * workingDayMonth;
                break;
            //季度
            case SEASON:
                value = 3 * fixedValue * workingDayMonth;
                break;
            //本月，某一月---//某年某月
            case MONTH:
            //上月
            case MONTH_LAST:
                //月目标 = 每月天数*每天固定频率
                value = fixedValue * workingDayMonth;
                break;
            //周
            case WEEK:
            //上周
            case WEEK_LAST:
                //周目标 = 每周天数*每天固定频率
                value = fixedValue * workingDayWeek;
                break;
            //本日or某一日
            case DAY:
            //昨日
            case DAY_LAST:
                value = fixedValue;
                break;
            //自定义---自己选择开始结束时间
            case CUSTOM:
                Integer count = (endTime - startTime) / TimeConstant.SECONDS_PER_DAY;
                count = (count == 0)? Integer.valueOf(1): count;
                //自定义时间目标 = 每天固定频率 * （总天数）
                value = fixedValue * count;
                break;
            default:
                break;
        }
        return value;
    }

    /**
     * 处理筛选不同时间维度时，最小考核周期为“月”的动态数值型目标
     * 规则：
     * （1）筛选月、季度、半年、年，直接展示相应的目标值
     * （2）筛选周，月目标÷每月天数×每周天数
     * （3）筛选天，月目标÷每月天数
     *
     * @param targetObject 目标
     * @param fiscalYearTimePojo 筛选时间pojo
     * @param timeTypeEnum 时间类型枚举
     * @param workingDayWeek 每周工作日天数
     * @param workingDayMonth 每月工作日天数
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 10:02
     * @since v1.0
     * @version v1.0
     */
    private static Double calcChangeValue4Month(JSONObject targetObject, FiscalYearTimePojo fiscalYearTimePojo,
                                                TimeTypeEnum timeTypeEnum, Double workingDayWeek, Double workingDayMonth) throws XbbException {
        Double value = -1D;
        switch (timeTypeEnum) {
            //全年
            case YEAR:
            //上半年
            case YEAR_HALF_UP:
            //下半年
            case YEAR_HALF_DOWN:
                value = targetObject.getDouble(timeTypeEnum.getAlias());
                value = (value == null)? Double.valueOf(-1): value;
                break;
            //季度
            case SEASON:
                Integer season = fiscalYearTimePojo.getSeason();
                JSONObject seasonObject = targetObject.getJSONObject(timeTypeEnum.getAlias());
                if (seasonObject != null) {
                    value = seasonObject.getDouble("s" + season);
                    value = (value == null)? Double.valueOf(-1): value;
                }
                break;
            //本月、某一月
            case MONTH:
            //上月
            case MONTH_LAST:
                Integer month = fiscalYearTimePojo.getMonth();
                if (month == 0) {
                    //本月
                    month = DateUtil.getMonth4Someday(DateUtil.getInt());
                }
                JSONObject monthObject = targetObject.getJSONObject(TimeTypeEnum.MONTH.getAlias());
                if (monthObject != null) {
                    value = monthObject.getDouble("m" + month);
                    value = (value == null)? Double.valueOf(-1): value;
                }
                break;
            //本周
            case WEEK:
            //上周
            case WEEK_LAST:
                monthObject = targetObject.getJSONObject(TimeTypeEnum.MONTH.getAlias());
                if (monthObject != null) {
                    //此时的月份已经在外部处理过，已经为财年月份
                    Integer monthIndex = fiscalYearTimePojo.getMonth();
                    Double monthValue = monthObject.getDouble("m" + monthIndex);
                    if (monthValue != null && monthValue != -1) {
                        //本周目标=本月目标÷每月天数×每周天数
                        value = Arith.div(monthValue, workingDayMonth) * workingDayWeek;
                    }
                }
                break;
            //本日or某一日
            case DAY:
            //昨日
            case DAY_LAST:
                monthObject = targetObject.getJSONObject(TimeTypeEnum.MONTH.getAlias());
                if (monthObject != null) {
                    //此时的月份已经在外部处理过，已经为财年月份
                    Integer monthIndex = fiscalYearTimePojo.getMonth();
                    Double monthValue = monthObject.getDouble("m" + monthIndex);
                    if (monthValue != null && monthValue != -1) {
                        //天目标 = 月目标÷每月天数
                        value = Arith.div(monthValue, workingDayMonth);
                    }
                }
                break;
            default:
                break;
        }
        return value;
    }


    /**
     * 处理筛选不同时间维度时，最小考核周期为“周”的动态数值型目标
     * 规则：
     * （1）筛选月、季度、半年、年，直接展示相应的目标值
     * （2）筛选周，转化成财年对应周，直接获取
     * （3）筛选天，周目标÷周的天数
     *
     * @param targetObject 目标
     * @param fiscalYearTimePojo 筛选时间pojo
     * @param timeTypeEnum 时间类型枚举
     * @param workingDayWeek 每周工作日天数
     * @param fiscalStartTime 财年起始时间戳
     * @return
     * @throws 
     * 
     * @author hongxiao
     * @date: 2019-02-21 10:04
     * @since v1.0
     * @version v1.0
     */
    private static Double calcChangeValue4Week(JSONObject targetObject, FiscalYearTimePojo fiscalYearTimePojo,
                                               TimeTypeEnum timeTypeEnum, Double workingDayWeek, Integer fiscalStartTime)
            throws XbbException {
        Double value = -1D;
        switch (timeTypeEnum) {
            //全年
            case YEAR:
            //上半年
            case YEAR_HALF_UP:
            //下半年
            case YEAR_HALF_DOWN:
            //季度
            case SEASON:
            //本月、某一月
            case MONTH:
            //上月
            case MONTH_LAST:
                //筛选月、季度、半年、年，直接展示相应的目标值
                value = calcChangeValue4Month(targetObject, fiscalYearTimePojo, timeTypeEnum, workingDayWeek, 1D);
                break;
            //本周
            case WEEK:
                //周目标，获取全年内相应周的目标
                //本周起始时间
                Integer weekStart = DateUtil.getThisWeekMorning();
                //本周是财年第几周。此时的fiscalStartTime已经是处理过财年年份fiscalYear后算出的结果
                Integer weekNum = DateUtil.getWeekNum(fiscalStartTime, weekStart);
                JSONObject weekObject = targetObject.getJSONObject(TimeTypeEnum.WEEK.getAlias());
                if (weekObject != null) {
                    value = weekObject.getDouble("w" + weekNum);
                    value = (value == null)? Double.valueOf(-1): value;
                }
                break;
            //上周
            case WEEK_LAST:
                Integer lastWeekStart = DateUtil.getThisWeekMorning() - TimeConstant.SECONDS_PER_WEEK;
                //上周是财年第几周。此时的fiscalStartTime已经是处理过财年年份fiscalYear后算出的结果
                weekNum = DateUtil.getWeekNum(fiscalStartTime, lastWeekStart);
                weekObject = targetObject.getJSONObject(TimeTypeEnum.WEEK.getAlias());
                if (weekObject != null) {
                    value = weekObject.getDouble("w" + weekNum);
                    value = (value == null)? Double.valueOf(-1): value;
                }
                break;
            //本日or某一日
            case DAY:
                //今日目标=周目标÷周的天数
                //本周起始时间
                weekStart = DateUtil.getThisWeekMorning();
                //本周是财年第几周。此时的fiscalStartTime已经是处理过财年年份fiscalYear后算出的结果
                weekNum = DateUtil.getWeekNum(fiscalStartTime, weekStart);
                weekObject = targetObject.getJSONObject(TimeTypeEnum.WEEK.getAlias());
                if (weekObject != null) {
                    value = weekObject.getDouble("w" + weekNum);
                    if (value != null && value != -1) {
                        //今日目标=周目标÷周的天数
                        value = Arith.div(value, workingDayWeek);
                    }
                }
                break;
            //昨日
            case DAY_LAST:
                //昨日所在周
                weekStart = DateUtil.getWeekMorningInSomeday(DateUtil.getTodayInt() - TimeConstant.SECONDS_PER_DAY);
                //昨日所在周是财年第几周。此时的fiscalStartTime已经是处理过财年年份fiscalYear后算出的结果
                weekNum = DateUtil.getWeekNum(fiscalStartTime, weekStart);
                weekObject = targetObject.getJSONObject(TimeTypeEnum.WEEK.getAlias());
                if (weekObject != null) {
                    value = weekObject.getDouble("w" + weekNum);
                    if (value != null && value != -1) {
                        //日目标=周目标÷周的天数
                        value = Arith.div(value, workingDayWeek);
                    }
                }
                break;
            default:
                break;
        }
        return value;
    }

    /**
     * 获取登录员工能看到的指标list（根据考核对象-超管看到所有的）
     * @param userEntity
     * @param list
     * @return java.util.List<com.xbongbong.pro.domain.entity.ChartEntity>
     * @throws XbbException
     * @author xinpeng.jiang
     * @date 2022/3/15 14:01
     * @version 1.0
     */
    private List<ChartEntity> getViewList(UserEntity userEntity, List<ChartEntity> list) {
        // 用户数据权限
        Integer dataPermission = userModel.getDataPermission(userEntity, true);
        return getViewList(false, userEntity, dataPermission, list);
    }

    /**
     * 获取考核对象名称
     *
     * @param list 考核对象列表
     * @return
     * @throws
     * @author hongxiao
     * @date: 2019-01-31 13:37
     * @version v1.0
     * @since v1.0
     */
    public String getPermissions2DepNames(List<IdNamePojo> list) {
        // 自定义部门处理
        if (list == null || list.size() == 0) {
            return "";
        }
        StringBuffer depNames = new StringBuffer();
        for (IdNamePojo pojo : list) {
            if (StringUtil.isNotEmpty(depNames.toString())) {
                depNames.append("、");
            }
            depNames.append(pojo.getName());
        }
        return depNames.toString();
    }
    /**
     * 设置自定义指标的单位
     * @param chartEntity  图表实体
     * @return 返回单位
     */
    public String setUnit(ChartEntity chartEntity) {
        String unit = chartEntity.getOriginalUnit();
        if(Objects.equals(UnitEnum.CUSTOM.getAlias(),unit)){
            JSONObject explainJson = chartEntity.getExplains();
            if(explainJson != null && !explainJson.isEmpty()){
                unit = explainJson.getString("unitValue");
            }
        }else {
            UnitEnum unitEnum = UnitEnum.getByAlias(unit);
            if(unitEnum != null){
                unit = unitEnum.getName();
            }
        }
        return unit;
    }

    /**
     * 获取单个目标
     * @param statisticsType
     * @param corpid
     * @param userId
     * @param id
     * @return
     * @throws XbbException
     */
    public ChartEntity getSetEntity(Integer statisticsType,String corpid,Long id) throws XbbException {
        if (!StatisticsTypeEnum.getStatisticTypes().contains(statisticsType)){
            throw new XbbException(PerformanceErrorCodeEnum.API_ERROR_240030,PerformanceErrorCodeEnum.API_ERROR_240030.getMsg());
        }
        ChartEntity setEntity;
        try {
            if (Objects.equals(statisticsType,StatisticsTypeEnum.SYSTEM_STATISTIC.getCode())){
                setEntity = chartModel.getByKey(id, corpid);
            }else {
                setEntity = chartCustomModel.getByKey(id, corpid);
            }
        } catch (Exception e) {
            throw new XbbException(PerformanceErrorCodeEnum.API_ERROR_240033, PerformanceErrorCodeEnum.API_ERROR_240033.getMsg());
        }
        if (setEntity == null || setEntity.getDel() == 1) {
            throw new XbbException(PerformanceErrorCodeEnum.API_ERROR_240001, PerformanceErrorCodeEnum.API_ERROR_240001.getMsg());
        }

        if (!ChartTypeEnum.getPerformanceChartType4List().contains(setEntity.getChartType())) {
            throw new XbbException(PerformanceErrorCodeEnum.API_ERROR_240032, PerformanceErrorCodeEnum.API_ERROR_240032.getMsg());
        }
        return setEntity;
    }

    /**
     * 判断公司套餐是否拥有自定义图表的查看权限
     *
     * @param corpid  公司id
     * @param statisticsTypeEnum 图表类型
     * @return boolean
     * @author zyy
     * @date 2022/3/31
     * @version v1.0
     */
    public boolean filterPackagePermissions(String corpid,StatisticsTypeEnum statisticsTypeEnum) throws XbbException {
        if(StatisticsTypeEnum.SYSTEM_STATISTIC.equals(statisticsTypeEnum)){
            //系统图表不做套餐判断，默认有权限
            return true;
        }
        Integer feeType = packageHelp.getFeeType(corpid);
        if(PackageTypeEnum.ULTIMATE.getType() == feeType){
            //旗舰版才能自定义图表的查看权限
            return true;
        }
        return false;
    }

    /**
     * 数据拷贝，并在进行类型转换时固定字段的顺序
     *
     * @param source
     * @param target
     * @return com.alibaba.fastjson.JSONObject
     * @author zyy
     * @date 2022/3/22
     * @version v1.0
     */
    public JSONObject copyProperties(ChartEntity source, PerformanceChartLogOrderPojo target) {
        BeanUtil.copyProperties(source,target);
        return JSONObject.parseObject(JSONObject.toJSONString(target), Feature.OrderedField);
    }
}
